package miner

import (
	block "blockChain/singleBlock"
	"fmt"
	"time"

	"github.com/fatih/color"
)

// 被广播的消息
type Message struct {
	// 区分诚实节点/恶意节点
	dishonest bool
	sender    uint64
	newBlock  block.Block
	// 区分：发来的区块，是新建的区块，还是同步的区块？
	isRecord bool
}

// May need other fields
type Node struct {
	id uint64
	// key为node的ID，value为对应node的receiveChan
	peers       map[uint64]chan Message
	receiveChan chan Message
	bufferPool  map[string]block.Block
	// 挖矿难度系数
	OfficialThreshold string
	// 是否需要结束
	NotifyChan <-chan string
	// 最长链的尾节点
	longestChainTail block.Block
}

func (n *Node) InitializeWithPrefix(id uint64, peers map[uint64]chan Message, recvChan chan Message, threshold string, notify chan string, prefix map[string]block.Block) {
	// 1
	n.id = id
	// 2
	n.peers = peers
	// 3
	n.receiveChan = recvChan
	// 4
	n.OfficialThreshold = threshold
	// 5
	n.NotifyChan = notify
	// 6 bufferPool
	n.bufferPool = make(map[string]block.Block)
	for blockID, block := range prefix {
		n.bufferPool[blockID] = block
	}
	firstNode := block.Block{}
	firstNode.Initialize(block.EMPTY_BLOCK, threshold)
	firstNode.Height = 1
	firstNode.SetTime("2021年")
	firstNode.SetSolution(0)
	_, exist := n.bufferPool[firstNode.SelfHash]
	if !exist {
		color.Red("非法前缀：缺少创世块")
		n.bufferPool[firstNode.SelfHash] = firstNode
	} else {
		color.Green("前缀可用")
	}
	// 7
	n.longestChainTail = block.GetLongestChainTail(n.bufferPool)
}

func (n *Node) Initialize(id uint64, peers map[uint64]chan Message, recvChan chan Message, threshold string, notify chan string) {
	// 1
	n.id = id
	// 2
	n.peers = peers
	// 3
	n.receiveChan = recvChan
	// 4
	n.OfficialThreshold = threshold
	// 5
	n.NotifyChan = notify

	// 6
	firstNode := block.Block{}
	firstNode.Initialize(block.EMPTY_BLOCK, threshold)
	firstNode.Height = 1
	firstNode.SetTime("2021年")
	firstNode.SetSolution(0)
	n.bufferPool = make(map[string]block.Block)
	n.bufferPool[firstNode.SelfHash] = firstNode

	// 7
	n.longestChainTail = firstNode
}

// 诚实节点：在最长链上进行构造
func (n *Node) NormalRun() {
	fmt.Println("start node : ", n.id)
	if n.longestChainTail.Height == block.CIRCLE ||
		n.longestChainTail.Height == block.UNKNOWN_HEIGHT {
		fmt.Println("node", n.id, "无创世块")
		return
	}
	defer fmt.Printf("node %d: 退出\n", n.id)

	nextBlock := block.Block{}
	nextBlock.Initialize(n.longestChainTail.SelfHash, n.OfficialThreshold)

	// 检查缓冲区有无变化
	changed := make(chan struct{})

	// 每隔10s，诚实节点发送当前维护的最长链，以进行同步

	// 仅用于测试：检测区块链的实际长度
	defer func() {
		tmpBufferPool := make(map[string]block.Block)
		for blockID, tmpBlock := range n.bufferPool {
			tmpBlock.Height = block.UNKNOWN_HEIGHT
			tmpBufferPool[blockID] = tmpBlock
		}
		realLen := block.GetLongestChainTail(tmpBufferPool)
		if realLen == n.longestChainTail {
			fmt.Println("node", n.id, ": 区块链、缓冲区一致√，最长", realLen.Height)
		} else {
			fmt.Println("node", n.id, ": 区块链、缓冲区不一致，实际最长", realLen.Height)
		}
	}()

loop:
	for {
		select {
		// 1. 有无中止信号
		case <-n.NotifyChan:
			{
				break loop
			}
		// 2. 其他矿工发来新区快
		case msg := <-n.receiveChan:
			{
				if !msg.newBlock.IsValid() {
					fmt.Printf("node %d: 其他节点有新区块，不合格from %d\n", n.id, msg.sender)
					break
				}
				fmt.Printf("node %d: 其他节点有新区块，合格from: %d\n", n.id, msg.sender)
				msg.newBlock.Height = block.UNKNOWN_HEIGHT
				n.bufferPool[msg.newBlock.SelfHash] = msg.newBlock
				msg.newBlock.Height = block.CalculateHeight(n.bufferPool, msg.newBlock.SelfHash)
				// 缓冲区有变化
				go func() {
					changed <- struct{}{}
				}()
				if n.longestChainTail.Height == block.CIRCLE ||
					n.longestChainTail.Height == block.UNKNOWN_HEIGHT ||
					n.longestChainTail.Height < msg.newBlock.Height {
					n.longestChainTail = n.bufferPool[msg.newBlock.SelfHash]
					nextBlock = block.Block{}
					nextBlock.Initialize(n.longestChainTail.SelfHash, n.OfficialThreshold)
				}
			}
		// (仅用于测试)每隔2秒显示区块链的长度
		case <-changed:
			{
				fmt.Printf("node %d: 总区块数量: %d, 最长链: %d\n", n.id, len(n.bufferPool), n.longestChainTail.Height)
			}
		// 3. 安心挖矿
		default:
			{
				raw := fmt.Sprintf("%s%d", nextBlock.Previous, nextBlock.Nonce)
				check := block.CalculateHash([]byte(raw))
				if check <= nextBlock.BitsThreshold {
					nextBlock.Height = n.longestChainTail.Height + 1
					nextBlock.SetTime(time.Now().Format("15:04:05"))
					fmt.Println("node", n.id, ": 挖矿成功")
					n.Broadcast(Message{
						sender:    n.id,
						newBlock:  nextBlock,
						dishonest: false,
					})
					n.bufferPool[nextBlock.SelfHash] = nextBlock
					// 缓冲区有变化
					go func() {
						changed <- struct{}{}
					}()
					n.longestChainTail = nextBlock
					nextBlock = block.Block{}
					nextBlock.Initialize(n.longestChainTail.SelfHash, n.OfficialThreshold)
				} else {
					nextBlock.Nonce++
				}
			}
		}
	}
}

func (n *Node) Broadcast(msg Message) {
	for id, ch := range n.peers {
		if id == n.id {
			continue
		}
		ch <- msg
	}
}
